﻿using System.Collections.Generic;
using System.Modules.Contracts.DTO;
using System.Modules.Extensions;
using System.Reflection;
using Microsoft.SharePoint.Administration;

namespace System.Modules.SharePoint.Logging
{
    /// <summary>
    /// SharePoint Log Service
    /// </summary>
    public class SharePointLogService : SPDiagnosticsServiceBase
    {
        #region Constants
        private const string INFO = "Info";
        private const string ERROR = "Error";
        #endregion

        #region Read Only Fieds
        private static readonly string DIAGNOSTIC_NAME = Assembly.GetCallingAssembly().GetName().Name;
        #endregion

        #region Private Fields
        private static List<string> _products = new List<string>();
        private static SharePointLogService _current = new SharePointLogService();
        private static readonly List<SPDiagnosticsCategory> CATEGORIES = new List<SPDiagnosticsCategory> {new SPDiagnosticsCategory(INFO, TraceSeverity.Verbose, EventSeverity.Information), new SPDiagnosticsCategory(ERROR, TraceSeverity.Unexpected, EventSeverity.Error)};
        #endregion

        #region Constructor(s)
        private SharePointLogService() : base(DIAGNOSTIC_NAME, SPFarm.Local)
        {
            _products = new List<string>();
        }
        #endregion

        #region Private Properties
        private static SharePointLogService Current
        {
            get { return _current ?? (_current = new SharePointLogService()); }
        }

        private static SPDiagnosticsArea DefaultArea
        {
            get { return GetDiagnosticsArea(DefaultProduct); }
        }

        private static String DefaultProduct
        {
            get
            {
                Assembly assembly = EntryAssembly;
                AssemblyDetails details = assembly.GetAssemblyDetails();
                return details == null ? assembly.GetName().Name : details.ProductInfo.Product;
            }
        }

        private static Assembly EntryAssembly
        {
            get { return Assembly.GetEntryAssembly() ?? Assembly.GetCallingAssembly(); }
        }
        #endregion

        #region Public Properties
        /// <summary>
        /// Message (Log Message)
        /// </summary>
        public static string Message
        {
            set { LogMessage(value); }
        }

        /// <summary>
        /// Error (Log Error)
        /// </summary>
        public static string Error
        {
            set { LogError(value); }
        }
        #endregion

        #region Methods
        /// <summary>
        /// Provide Areas
        /// </summary>
        /// <returns></returns>
        protected override IEnumerable<SPDiagnosticsArea> ProvideAreas()
        {
            var areas = new List<SPDiagnosticsArea>();
            _products.ForEach(product => areas.Add(new SPDiagnosticsArea(product, CATEGORIES)));
            return areas;
        }

        private static SPDiagnosticsArea GetDiagnosticsArea(string product)
        {
            EnsureProduct(product);
            return Current.Areas[product];
        }

        private static void EnsureProduct(string product)
        {
            if (product != null && !_products.Exists(match => match.Equals(product, StringComparison.InvariantCultureIgnoreCase)))
            {
                _products.Add(product);
                List<string> products = _products;
                _current = new SharePointLogService();
                _products = products;
            }
        }

        /// <summary>
        /// This method logs message to SharePoint Log
        /// </summary>
        /// <param name="message"></param>
        public static void LogMessage(string message)
        {
            LogMessage(DefaultProduct, message);
        }

        /// <summary>
        /// This method logs message to SharePoint Log
        /// </summary>
        /// <param name="product"></param>
        /// <param name="message"></param>
        public static void LogMessage(string product, string message)
        {
            SPDiagnosticsArea defaultArea = GetDiagnosticsArea(product) ?? DefaultArea;
            if (defaultArea != null)
            {
                SPDiagnosticsCategory category = defaultArea.Categories[INFO];
                if (category != null)
                {
                    Current.WriteTrace(0, category, TraceSeverity.Verbose, message);
                }
            }
        }

        /// <summary>
        /// This method logs message to SharePoint Log
        /// </summary>
        /// <param name="message"></param>
        public static void LogError(string message)
        {
            LogError(DefaultProduct, message);
        }

        /// <summary>
        /// This method logs message to SharePoint Log
        /// </summary>
        /// <param name="product"></param>
        /// <param name="message"></param>
        public static void LogError(string product, string message)
        {
            SPDiagnosticsArea defaultArea = GetDiagnosticsArea(product) ?? DefaultArea;
            if (defaultArea != null)
            {
                SPDiagnosticsCategory category = defaultArea.Categories[ERROR];
                if (category != null)
                {
                    Current.WriteTrace(0, category, TraceSeverity.Unexpected, message);
                }
            }
        }
        #endregion
    }
}